#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <ctime>
#include <cstdarg>
#include <cstring>
#include <fstream>

#include <pthread.h>
#include "LockGuard.hpp"

namespace log_ns
{
    // 日志的等级
    enum
    {
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    // 将日志等级转换成字符串
    std::string LevelToString(int level)
    {
        switch (level)
        {
        case DEBUG:
            return "DEBUG";
        case INFO:
            return "INFO";
        case WARNING:
            return "WARNING";
        case ERROR:
            return "ERROR";
        case FATAL:
            return "FATAL";
        default:
            return "UNKNOWN";
        }
    }
    // struct tm {
    //     int tm_sec;    /* Seconds (0-60) */
    //     int tm_min;    /* Minutes (0-59) */
    //     int tm_hour;   /* Hours (0-23) */
    //     int tm_mday;   /* Day of the month (1-31) */
    //     int tm_mon;    /* Month (0-11) */
    //     int tm_year;   /* Year - 1900 */
    //     int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
    //     int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
    //     int tm_isdst;  /* Daylight saving time */
    // };

    std::string GetCurrentTime()
    {
        // 获得时间戳
        time_t now = time(nullptr);
        // std::cout << "now: " << now << std::endl;

        // localtime，通过时间戳，将时间戳转换成对应的cur_time
        struct tm *curr_time = localtime(&now);
        char buffer[128];

        snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d",
                 // 注意：这里的year是 - 1900后的值
                 curr_time->tm_year + 1900,
                 curr_time->tm_mon + 1,
                 curr_time->tm_mday,
                 curr_time->tm_hour,
                 curr_time->tm_min,
                 curr_time->tm_sec);
        // std::cout << buffer << std::endl;
        return buffer;
    }

    // 日志消息结构体
    class logmsg
    {
    public:
        std::string _level;
        pid_t _id; // 进程id
        std::string _filename;
        int _filenumber;
        std::string _curr_time;
        std::string _msg_info;
    };

#define SCREEN_TYPE 1
#define FILE_TYPE 2
    const std::string defaultlogfile = "./log.txt";
    pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER; // 定义一个全局锁

    // log.logmsg("", 12, INFO, "this is a %d message", x);
    // 日志
    class Log
    {
    public: // 给一个文件缺省值，默认向显示器打印
        Log(const std::string &logfile = defaultlogfile)
            : _logfile(logfile), _type(SCREEN_TYPE)
        {
        }

        // 在未来，使用的人，选择打印方式，是朝显示器打印还是朝文件中打印
        void Enable(int type)
        {
            _type = type;
        }
        void FlushLogToScreen(const logmsg &lg)
        {
            printf("[%s][%d][%s][%d][%s] %s",
                   lg._level.c_str(),
                   lg._id,
                   lg._filename.c_str(),
                   lg._filenumber,
                   lg._curr_time.c_str(),
                   lg._msg_info.c_str());
        }
        void FlushLogToFile(const logmsg &lg)
        {
            // 1.打开文件
            std::ofstream out(_logfile, std::ios::app);
            if (!out.is_open())
                return;
            char logtxt[2048];
            // 将日志信息，打印到缓冲区logtxt当中
            snprintf(logtxt, sizeof(logtxt), "[%s][%d][%s][%d][%s] %s",
                     lg._level.c_str(),
                     lg._id,
                     lg._filename.c_str(),
                     lg._filenumber,
                     lg._curr_time.c_str(),
                     lg._msg_info.c_str());
            out.write(logtxt, strlen(logtxt));
            out.close();
        }

        // 日志往哪打印
        void FlushLog(const logmsg &lg)
        {
            // 加过滤逻辑：程序在开发期间，要打印DEBUG信息，非开发期间，不打印过滤掉

            LockGuard lock(&glock);
            switch (_type)
            {
            case SCREEN_TYPE:
                FlushLogToScreen(lg);
                break;
            case FILE_TYPE:
                FlushLogToFile(lg);
                break;
            default:
                break;
            }
        }

        // 传入的格式     //可变参数
        void LogMsg(std::string filename, int filenumber, int level, const char *format, ...)
        {
            logmsg lg;
            lg._level = LevelToString(level);

            lg._id = getpid();
            lg._filename = filename;
            lg._filenumber = filenumber;
            // 需要获得的是当前时间，从系统中获取，gettimeofday（），返回值是timevalue是一个时间戳，或者直接使用c语言提供的time（）
            lg._curr_time = GetCurrentTime();
            // 用户传进来的msg
            //  捕获可变部分
            //  #include <stdarg.h>

            // int vprintf(const char *format, va_list ap);
            // int vfprintf(FILE *stream, const char *format, va_list ap);
            // int vdprintf(int fd, const char *format, va_list ap);
            // int vsprintf(char *str, const char *format, va_list ap);
            // int vsnprintf(char *str, size_t size, const char *format, va_list ap);

            va_list ap;
            va_start(ap, format);
            char log_info[1024];
            // 会按照格式将可变部分提取出来，放到log_info里
            vsnprintf(log_info, sizeof(log_info), format, ap);
            va_end(ap);

            lg._msg_info = log_info;
            // std::cout << lg._msg_info << std::endl;

            // 打印出日志：
            FlushLog(lg);
        }
        ~Log()
        {
        }

    private:
        int _type;
        std::string _logfile;
    };

    Log lg;

#define LOG(Level, Format, ...)                                      \
    do                                                               \
    {                                                                \
        lg.LogMsg(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__); \
    } while (0)
#define EnableScreen()          \
    do                          \
    {                           \
        lg.Enable(SCREEN_TYPE); \
    } while (0)
#define EnableFile()          \
    do                        \
    {                         \
        lg.Enable(FILE_TYPE); \
    } while (0)
}
